﻿using DynamicExpresso;
using RAP.Framework.Libary.Lock;
using RAP.Framework.Libary.Utils;
using RAP.Framework.Libary.Utils.Collection.Iterator;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using static RAP.Framework.Libary.DataSource.ServiceUtils;


namespace RAP.Framework.Libary.SqlTaskParser.Parsers
{
  /// <summary>
  /// 继承自ExpressionTokenParser，@mapping指令必须等待前面的查询任务执行完毕后再执行
  /// </summary>
  public class MappingTokenParser : ExpressionTokenParser
  {
    private static Interpreter _interpreter = new Interpreter();

    /// <summary>
    /// 无条件直接相等
    /// </summary>
    public const string DIRECT_EQUALS = "1==1";

    /// <summary>
    /// 
    /// </summary>
    private const string SYNTAX_ERROR_PREFIX = "@mapping 语法错误，";

    #region 定义内部类
    /// <summary>
    /// 主数据源
    /// </summary>
    private class DataSource
    {
      public string Expression { get; set; }
      public string Alias { get; set; }
      public Type Type { get; set; }

      private object _value = null;
      public object Value
      {
        get
        {
          return _value;
        }
        set
        {
          _value = value;
          if (value != null)
          {
            Type = value.GetType();
          }
        }
      }
    }

    /// <summary>
    /// 被关联的数据源
    /// </summary>
    private class JoinDataSource : DataSource, IDisposable
    {
      /// <summary>
      /// 缓存相同的OnHandler
      /// </summary>
      private static IDictionary<string, Func<object, object, object>> _onHandlerCaches = new ConcurrentDictionary<string, Func<object, object, object>>();

      /// <summary>
      /// 缓存相同的JoinHandler
      /// </summary>
      private static IDictionary<string, Action<object, object, RecursionIteratorContext>> _joinHandlerCaches = new ConcurrentDictionary<string, Action<object, object, RecursionIteratorContext>>();

      public string OnExpression { get; set; }
      public string IntoExpression { get; set; }

      private Func<object, object, object> OnHandler { get; set; }
      private Action<object, object, RecursionIteratorContext> JoinHandler { get; set; }

      private string GetTypeName(Type type)
      {
        return type.FullName.StartsWith("AspectCore.") ? type.BaseType.FullName : type.FullName;
      }

      protected bool IsOn(DataSource targetDataSource, object sourceData, object joinData)
      {
        if (OnHandler == null)
        {
          //在第一次执行关联时再来编译关联表达式
          var sourceAlias = targetDataSource.Alias;
          var sourceDataType = sourceData.GetType();
          var sourceDataTypeName = GetTypeName(sourceDataType);
          var joinAlias = Alias;
          var joinDataType = joinData.GetType();
          var joinDataTypeName = GetTypeName(joinDataType);
          var onHandler = OnHandler;
          var cacheKey = $"{OnExpression},{sourceAlias},{sourceDataTypeName};{joinAlias},{joinDataTypeName}";
          if (!_onHandlerCaches.TryGetValue(cacheKey, out onHandler))
          {
            if (sourceDataType.GetInterface("IDictionary") != null)
            {
              //如果是键值对 有待改进
              if (OnExpression == DIRECT_EQUALS)
              {
                onHandler = (sourceDataValue, joinDataValue) => { return true; };
              }
              else
              {
                var onLeftExpression = OnExpression.LeftSubstring(' ').Trim().LeftEndString('.');
                var onRightExpression = OnExpression.RightEndString(' ').Trim().LeftEndString('.');
                onHandler = (sourceDataValue, joinDataValue) =>
                {
                  if (onLeftExpression.IsNotNullOrEmpty() && onRightExpression.IsNotNullOrEmpty())
                  {
                    return Equals(sourceDataValue.GetValue(onLeftExpression), joinDataValue.GetValue(onRightExpression));
                  }
                  return true;
                };
              }
            }
            else
            {
              var lambda = _interpreter.Parse(
                OnExpression,
                new Parameter(sourceAlias, sourceDataType),
                new Parameter(joinAlias, joinDataType)
              );
              onHandler = (sourceDataValue, joinDataValue) =>
              {
                return lambda.Invoke(sourceDataValue, joinDataValue);
              };
            }
            _onHandlerCaches.TryAdd(cacheKey, onHandler);
          }
          OnHandler = onHandler;
        }
        return (bool)OnHandler.Invoke(sourceData, joinData);
      }

      public bool Join(DataSource targetDataSource, object sourceData, object joinData, RecursionIteratorContext iteratorContext)
      {
        var continueJoin = true;
        if (IsOn(targetDataSource, sourceData, joinData))
        {
          if (JoinHandler == null)
          {
            //在第一次执行Join时再来编译赋值表达式
            var sourceDataType = sourceData.GetType();
            var isDictionary = (sourceDataType.GetInterface("IDictionary") != null);
            var joinHandler = JoinHandler;
            var joinExpression = IntoExpression.LeftEndString('.');
            var joinDataType = joinData.GetType();
            var targetPropertyType = isDictionary ? typeof(IDictionary) : sourceDataType.GetPropertyType(joinExpression);
            var targetPropertyTypeName = GetTypeName(targetPropertyType);
            var targetDataSourceTypeName = GetTypeName(targetDataSource.Type);
            var joinDataTypeName = GetTypeName(joinDataType);
            if (isDictionary)
            {
              //基础值对象或键值对对象
              joinHandler = (sourceDataValue, joinDataValue, context) =>
              {
                if (context.IsCollection)
                {
                  var dataSourceList = sourceDataValue.GetValue<IList>(joinExpression);
                  if (dataSourceList == null)
                  {
                    //检测键值对的属性是否为null，如果为null则创建一个List<T>
                    dataSourceList = typeof(List<>).MakeGenericType(joinDataType).CreateInstance<IList>();
                    sourceDataValue.SetValue(joinExpression, dataSourceList);
                  }
                  dataSourceList.Add(joinDataValue);
                }
                else
                {
                  sourceDataValue.SetValue(joinExpression, joinDataValue);
                }
              };
            }
            else
            {
              //实体对象
              var cacheKey = $"{joinExpression},{targetDataSource.Alias},{targetDataSourceTypeName},{targetPropertyTypeName},{IntoExpression},{joinDataTypeName}";
              if (!_joinHandlerCaches.TryGetValue(cacheKey, out joinHandler))
              {
                if (
                  targetPropertyType.IsArray ||
                  targetPropertyType.Name == "IList`1" ||
                  targetPropertyType.GetInterface("IList") != null)
                {
                  //如果是集合，则将关联到的数据添加到集合中
                  var listType = typeof(List<>).MakeGenericType(joinDataType.FullName.StartsWith("AspectCore.") ? joinDataType.BaseType : joinDataType);

                  //添加实体到集合的表达式
                  var joinLambda = _interpreter.Parse(
                    $"{IntoExpression}.Add(_joinData)",
                    new Parameter(targetDataSource.Alias, targetDataSource.Type),
                    new Parameter("_joinData", joinDataType)
                  );

                  //检测集合表达式
                  var checkNullLambda = _interpreter.Parse(
                    $"{IntoExpression}==null",
                    new Parameter(targetDataSource.Alias, targetDataSource.Type)
                  );

                  //创建集合表达式
                  var createListLambda = _interpreter.Parse(
                    $"{IntoExpression}=value",
                    new Parameter(targetDataSource.Alias, targetDataSource.Type),
                    new Parameter("value", listType)
                  );
                  joinHandler = (sourceDataValue, joinDataValue, context) =>
                  {
                    if (Equals(checkNullLambda.Invoke(sourceDataValue), true))
                    {
                      //如果集合是null，则创建一个新的List<T>集合，T为实体类型
                      createListLambda.Invoke(sourceDataValue, listType.CreateInstance<IList>());
                    }
                    joinLambda.Invoke(sourceDataValue, joinDataValue);
                  };
                }
                else
                {
                  var setValueLambda = _interpreter.Parse(
                    $"{IntoExpression} = value",
                    new Parameter(targetDataSource.Alias, targetDataSource.Type),
                    new Parameter("value", joinDataType)
                  );
                  continueJoin = false;//如果是对指定属性赋值，则不再继续关联
                  joinHandler = (sourceDataValue, joinDataValue, context) =>
                  {
                    setValueLambda.Invoke(sourceDataValue, joinDataValue);
                  };
                }
              }
              _joinHandlerCaches.TryAdd(cacheKey, joinHandler);
            }
            JoinHandler = joinHandler;
          }
          JoinHandler(targetDataSource.Value, joinData, iteratorContext);
        }
        return continueJoin;
      }

      public void Dispose()
      {
        OnHandler = null;
        JoinHandler = null;
      }
    }
    #endregion 定义内部类

    /// <summary>
    /// 解析主数据源
    /// </summary>
    /// <param name="exp"></param>
    /// <returns></returns>
    private DataSource ParseDataSource(string exp)
    {
      var dataSourceExp = exp.LeftSubstring(AS_SYNTAX)?.Trim();
      var alias = exp.RightEndString(AS_SYNTAX)?.Trim().IsNullOrEmptyValue(dataSourceExp);
      if (dataSourceExp.IsNullOrEmpty() || alias.IsNullOrEmpty())
      {
        throw new Exception($"{SYNTAX_ERROR_PREFIX}缺少数据源定义。");
      }

      return new DataSource
      {
        Expression = dataSourceExp,
        Alias = alias
      };
    }

    /// <summary>
    /// 解析被关联的数据源
    /// </summary>
    /// <param name="expArr"></param>
    /// <returns></returns>
    private IList<JoinDataSource> ParseJoinDataSources(IList<string> expArr)
    {
      var joinDataSources = new List<JoinDataSource>();

      foreach (var exp in expArr)
      {
        var dataSourceExp = exp.LeftSubstring(AS_SYNTAX, exp.LeftSubstring(INTO_SYNTAX))?.Trim();
        var alias = exp.MidSubstring(AS_SYNTAX, " ")?.Trim();
        var on = exp.MidSubstring(ON_SYNTAX, INTO_SYNTAX)?.Trim();
        var into = exp.RightEndString(INTO_SYNTAX)?.Trim();
        var expression = exp.LeftSubstring(AS_SYNTAX, exp.LeftSubstring(INTO_SYNTAX))?.Trim();
        if (dataSourceExp.IsNullOrEmpty() || into.IsNullOrEmpty() || expression.IsNullOrEmpty())
        {
          throw new NullReferenceException($"{SYNTAX_ERROR_PREFIX}join 缺少必要参数！");
        }
        joinDataSources.Add(new JoinDataSource
        {
          Expression = expression,
          Alias = alias,
          OnExpression = ParseCSharpExpression(on).IsNullOrEmptyValue(() => DIRECT_EQUALS),
          IntoExpression = into
        });
      }

      return joinDataSources;
    }

    public override void ParseToken(SqlParseContext parseContext)
    {
      var token = parseContext.CurrentToken;
      var tokenParam = token.Param?.Trim();
      if (tokenParam.IsNullOrEmpty())
      {
        return;
      }
      parseContext.CurrentTask.OnSuccess((task, parseResult, context) =>
      {
        var results = context.ResultData;
        if (results == null || results.Count == 0)
        {
          return;
        }
        /*
        Citys as c 
          join Schools as s on c.Id = s.CityId into c.Schools 
          join Teachers as t on c.Id = t.CityId into c.Teachers;
        */
        var expArr = new List<string>(token.Param.Split(new string[] { JOIN_SYNTAX }, StringSplitOptions.RemoveEmptyEntries));
        if (expArr.Count < 2)
        {
          throw new Exception($"{SYNTAX_ERROR_PREFIX}缺少join关键字。");
        }
        var targetDataSource = ParseDataSource(expArr.Shift<string>());
        var joinDataSources = ParseJoinDataSources(expArr);
        var targetDataSourceList = new RecursionIterator<object>(results, targetDataSource.Expression);
        foreach (var targetDataItem in targetDataSourceList)
        {
          var targetData = targetDataItem.Value;
          if (targetData == null)
          {
            continue;
          }
          targetDataSource.Value = targetDataItem.Context.CurrentData;
          foreach (var joinDataSource in joinDataSources)
          {
            if (joinDataSource.Expression.IsNullOrEmpty())
            {
              continue;
            }
            var joinDataSourceList = new RecursionIterator<object>(results, joinDataSource.Expression);
            //if (joinDataSource.OnExpression != DIRECT_EQUALS)
            //{

            //}
            //else
            //{

            //}
            foreach (var joinDataItem in joinDataSourceList)
            {

              if (!joinDataSource.Join(targetDataSource, targetData, joinDataItem.Value, joinDataItem.Context))
              {
                break;
              }
            }
          }
        }
      });
    }
  }
}
